﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PocketGPW.PocketGPWDataSetTableAdapters;
using System.Threading;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Globalization;

namespace PocketGPW
{
    public partial class Main : Form
    {
        private static readonly Regex _rxIndices = new Regex(@"((|\s)*)(</td><td.*?>)(?<value>(-|\d|\,|\:|\/|\s)+)|(href\s*=\s*(?:\""\S*(?<value>PL\d{10})\S*\""))", RegexOptions.IgnoreCase | RegexOptions.Multiline);
        private static readonly Regex _rxQuotations = new Regex(@"(<td>(?<value>(\+|-|\d|\,|\:|\/|\s)+)</td>)|<td.*?>\S*\s\((?<value>(\+|-|\d|\,)+)\%\)</td>", RegexOptions.IgnoreCase | RegexOptions.Multiline);

        private const float ColumnNameRatio = (float)85 / (float)240;
        private const float ColumnLastRatio = (float)50 / (float)240;
        private const float ColumnChangeRatio = (float)45 / (float)240;
        private const float ColumnAlertRatio = (float)15 / (float)240;
        private const float ColumnNoteRatio = (float)15 / (float)240;

        private StockEditor _stockEditor;

        private Thread _thread;
        private bool _threadInProgress;
        private bool _forceThreadRefresh;
        private int _threadSecondsCounter;

        private Notification _notification;

        private DataGridNoteTextBoxColumn _dgntbcNote;
        private DataGridAlertTextBoxColumn _dgatbcAlert;
        private DataGridChangeTextBoxColumn _dgctbcChange;

        public Main()
        {
            InitializeComponent();

            Icon = Properties.Resources.Icon;

            // custom kolumna dla datagrida (nie moze byc w designerze bo nie obsługuje customowych)
            _dgctbcChange = new DataGridChangeTextBoxColumn(_dg);
            _dgctbcChange.Format = "";
            _dgctbcChange.FormatInfo = null;
            _dgctbcChange.HeaderText = "Zmiana";
            _dgctbcChange.MappingName = "Change";
            _dgctbcChange.NullText = "";
            _dgctbcChange.Width = 45;
            _dgts.GridColumnStyles.Add(this._dgctbcChange);

            _dgatbcAlert = new DataGridAlertTextBoxColumn(_dg);
            _dgatbcAlert.Format = "";
            _dgatbcAlert.FormatInfo = null;
            _dgatbcAlert.HeaderText = " ";
            _dgatbcAlert.MappingName = "Alert";
            _dgatbcAlert.NullText = "";
            _dgatbcAlert.Width = 15;
            _dgts.GridColumnStyles.Add(this._dgatbcAlert);

            _dgntbcNote = new DataGridNoteTextBoxColumn(_dg);
            _dgntbcNote.Format = "";
            _dgntbcNote.FormatInfo = null;
            _dgntbcNote.HeaderText = " ";
            _dgntbcNote.MappingName = "Note";
            _dgntbcNote.NullText = "";
            _dgntbcNote.Width = 15;
            _dgts.GridColumnStyles.Add(this._dgntbcNote);
        }

        private void Main_Load(object sender, EventArgs e)
        {
            _taStocks.Fill(_ds.Stocks);

            // notyfikacja
            _notification = new Notification();
            _notification.Caption = "Pocket GPW";
            _notification.Critical = false;
            _notification.TitleTime = true;
            _notification.Icon = Properties.Resources.Icon;
            _notification.LeftSoftKey = new NotificationSoftKey(NotificationSoftKeyType.Dismiss, "View");
            _notification.RightSoftKey = new NotificationSoftKey(NotificationSoftKeyType.Dismiss, "Dismiss");
            _notification.LeftSoftKeyClick += new EventHandler(_notification_LeftSoftKeyClick);
            
            // watek pobierania
            _thread = new Thread(new ThreadStart(Run));
            _thread.IsBackground = true;
            _thread.Start();
        }

        private void Main_Closing(object sender, CancelEventArgs e)
        {
            if (_thread != null)
            {
                try
                {
                    _thread.Abort();
                    _thread = null;
                }
                catch { }
            }
        }

        private void Main_Resize(object sender, EventArgs e)
        {
            // wywołanie przed konstruktorem
            if (_dgatbcAlert == null || _dgntbcNote == null)
                return;

            // dopasowuje kolumny do widoku
            int screenWidth = Screen.PrimaryScreen.WorkingArea.Width;

            _dgtbcName.Width = (int)(screenWidth * ColumnNameRatio);
            _dgtbcLast.Width = (int)(screenWidth * ColumnLastRatio);
            _dgctbcChange.Width = (int)(screenWidth * ColumnChangeRatio);
            _dgatbcAlert.Width = (int)(screenWidth * ColumnAlertRatio);
            _dgntbcNote.Width = (int)(screenWidth * ColumnNoteRatio);
        }

        private void _miExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void _miAbout_Click(object sender, EventArgs e)
        {
            using (About about = new About())
            {
                about.ShowDialog(this, " - informacje");
            }
        }

        private void _miAdd_Click(object sender, EventArgs e)
        {
            StockEdit();
        }

        private void _miRemove_Click(object sender, EventArgs e)
        {
            StockRemove();
        }

        private void _miView_Click(object sender, EventArgs e)
        {
            StockView();
        }

        private void _notification_LeftSoftKeyClick(object sender, EventArgs e)
        {
            try
            {
                this.Activate();
            }
            catch { }
        }

        private void StockView()
        {
            if (_dg.CurrentRowIndex < 0)
                return;

            DataRowView drv = _ds.Stocks.DefaultView[_dg.CurrentRowIndex];

            using (StockViewer dv = new StockViewer(_ds, _taStocks, drv["StockId"].ToString()))
            {
                dv.ShowDialog(this, " - " + drv["StockId"]);
            }
            
            _dg.Invalidate();
        }

        private void StockRemove()
        {
            if (_dg.CurrentRowIndex < 0)
                return;

            _ds.Stocks.DefaultView.Delete(_dg.CurrentRowIndex);
            _taStocks.Update(_ds.Stocks); // update
        }

        private void StockEdit()
        {
            if (_stockEditor == null)
                _stockEditor = new StockEditor(_ds, _taStocks);

            _stockEditor.Show();
        }

        private void Run()
        {
            Thread.Sleep(5000);

            while (true)
            {
                _threadInProgress = true;

                this.BeginInvoke(new Action<string>(StatusBarText), new object[] { "Pobieranie notowań..." });
                this.BeginInvoke(new Action<bool>(StatusBarVisibility), new object[] { true });

                // iteruj indexy
                try
                {
                    DownloadIndices();
                    foreach (DataRow dr in _ds.Stocks.Rows)
                        switch (dr["Type"].ToString())
                        {
                            case "S": DownloadStock(dr["StockId"].ToString());
                                break;
                        }
                }
                catch {}

                this.BeginInvoke(new Action<bool>(StatusBarVisibility), new object[] { false });

                _threadInProgress = false;

                while (
                    !_forceThreadRefresh // ręcznie odświeżenie
                    &&
                    (
                        (SettingsHelper.GetInt32(SettingsHelper.Keys.Refresh) != 0 && _threadSecondsCounter < SettingsHelper.GetInt32(SettingsHelper.Keys.Refresh) * 60) // odświeżanie z ustawień
                        ||
                        (SettingsHelper.GetInt32(SettingsHelper.Keys.Refresh) == 0) // ręczne odświeżanie
                    )
                )
                {

                    if (SettingsHelper.GetInt32(SettingsHelper.Keys.Refresh) == 0)
                        _threadSecondsCounter = 0; // kasuj licznik na warunku manualnym
                    else
                        _threadSecondsCounter++; // inkrementuj na automacie

                    Thread.Sleep(1000);
                }                

                // kasowanie licznika / flagi
                _forceThreadRefresh = false;
                _threadSecondsCounter = 0;
            }
        }

        private void DownloadIndices()
        {
            HttpWebRequest request;
            HttpWebResponse response;

            request = HTTPHelper.CreateRequest("http://www.gpw.pl/ajaxindex.php?action=GPWIndexes&start=showTable&tab=indexes&lang=PL&target_page_portfolio=portfele_indeksow&full=0");
            if (request == null)
                return;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch
            {
                return;
            }

            string responseString = HTTPHelper.ReadResponse(response).Replace("<![CDATA[", string.Empty).Replace("]]>", string.Empty).Replace("&nbsp;", string.Empty);

            response.Close();
            request.Abort();
            response = null;
            request = null;

            if (responseString.Length == 0)
                return;

            PocketGPWDataSet.StocksRow sr = null;

            NumberFormatInfo provider = new NumberFormatInfo();
            provider.NumberDecimalSeparator = ",";
            provider.NumberGroupSeparator = " ";

            MatchCollection ma = _rxIndices.Matches(responseString);
            for (int i = 0; i < ma.Count; i++)
            {
                Match m = ma[i];
                if (m.Groups.Count > 0 && m.Groups["value"].Value != "---")
                {
                    switch (i % 11)
                    {
                        case 0:
                            {
                                sr = _ds.Stocks.FindByStockId(m.Groups["value"].Value);
                                break;
                            }
                        case 2:
                            {
                                if (sr != null)
                                    sr.SetField("Time", m.Groups["value"].Value);
                                break;
                            }

                        case 4:
                            {
                                if (sr != null)
                                    sr.SetField("Open", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 5:
                            {
                                if (sr != null)
                                    sr.SetField("Low", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 6:
                            {
                                if (sr != null)
                                    sr.SetField("High", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 7:
                            {
                                if (sr != null)
                                    sr.SetField("Last", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 8:
                            {
                                if (sr != null)
                                    sr.SetField("Change", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 10:
                            {
                                if (sr != null)
                                    sr.SetField("TurnoverValue", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        //case 2:
                        //    {
                        //        sr.SetField("Volume", Convert.ToInt32(m.Groups["value"].Value, provider);
                        //        break;
                        //    }
                    }
                }

                if (sr != null && i % 11 == 10)
                {
                    sr.SetField("Alert", false); // wykasowanie flagi

                    // check alarm
                    PocketGPWDataSet.AlertsRow[] alerts = sr.GetAlertsRows();
                    foreach (PocketGPWDataSet.AlertsRow alert in alerts)
                    {
                        double currentValue = Convert.ToDouble(sr[ConvertParameterToColumn(alert["Parameter"].ToString())]);

                        if (CheckAlert(currentValue, alert))
                        {
                            sr.SetField("Alert", true);
                            // show notification
                            BeginInvoke(new Action<string, double, PocketGPWDataSet.AlertsRow>(ShowNotification), new object[] { sr["Name"].ToString(), currentValue, alert });
                            //break foreach
                            break;
                        }
                    }
                }
            }

            _taStocks.Update(_ds.Stocks);

            BeginInvoke(new Action(_dg.Invalidate));

        }

        private void DownloadStock(string id)
        {
            HttpWebRequest request;
            HttpWebResponse response;

            request = HTTPHelper.CreateRequest(String.Format("http://www.gpw.pl/ajaxindex.php?action=GPWListaSp&start=quotationsTab&gls_isin={0}&lang=PL", id));
            if (request == null)
                return;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch
            {
                return;
            }

            string responseString = HTTPHelper.ReadResponse(response).Replace("<![CDATA[", string.Empty).Replace("]]>", string.Empty).Replace("&nbsp;", string.Empty).Replace(".", ",");

            response.Close();
            request.Abort();
            response = null;
            request = null;

            if (responseString.Length == 0)
                return;

            PocketGPWDataSet.StocksRow sr = _ds.Stocks.FindByStockId(id);
            if (sr == null)
                return;

            NumberFormatInfo provider = new NumberFormatInfo();
            provider.NumberDecimalSeparator = ",";
            provider.NumberGroupSeparator = " ";

            #region parse response

            MatchCollection ma = _rxQuotations.Matches(responseString);
            for (int i = 0; i < ma.Count; i++)
            {
                Match m = ma[i];
                if (m.Groups.Count > 0)
                {
                    switch (i)
                    {
                        //case 2:
                        //    {
                        //        if (sr != null)
                        //            sr.SetField("Time", m.Groups["value"].Value);
                        //        break;
                        //    }

                        //case 4:
                        //    {
                        //        if (sr != null)
                        //            sr.SetField("Open", Convert.ToDecimal(m.Groups["value"].Value));
                        //        break;
                        //    }

                        case 0:
                            {
                                if (sr != null)
                                    sr.SetField("Last", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 1:
                            {
                                if (sr != null)
                                    sr.SetField("Change", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 4:
                            {
                                if (sr != null)
                                    sr.SetField("Low", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 5:
                            {
                                if (sr != null)
                                    sr.SetField("High", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }

                        case 6:
                            {
                                sr.SetField("Volume", Convert.ToInt32(Convert.ToDecimal(m.Groups["value"].Value, provider)));
                                break;
                            }

                        case 7:
                            {
                                if (sr != null)
                                    sr.SetField("TurnoverValue", Convert.ToDecimal(m.Groups["value"].Value, provider));
                                break;
                            }
                    }
                }
            }
            #endregion

            sr.SetField("Alert", false); // wykasowanie flagi

            // check alarm
            PocketGPWDataSet.AlertsRow[] alerts = sr.GetAlertsRows();
            foreach (PocketGPWDataSet.AlertsRow alert in alerts)
            {
                double currentValue = Convert.ToDouble(sr[ConvertParameterToColumn(alert["Parameter"].ToString())]);

                if (CheckAlert(currentValue, alert))
                {
                    sr.SetField("Alert", true);
                    // show notification
                    BeginInvoke(new Action<string, double, PocketGPWDataSet.AlertsRow>(ShowNotification), new object[] {sr["Name"].ToString(), currentValue, alert});
                    //break foreach
                    break;
                }
            }

            _taStocks.Update(_ds.Stocks);

            BeginInvoke(new Action(_dg.Invalidate));
        }

        private bool CheckAlert(double currentValue, PocketGPWDataSet.AlertsRow alert)
        {            
            double alertValue = Convert.ToDouble(alert["Value"]);
            string comparision = alert["Comparision"].ToString();

            switch (comparision)
            {
                case "<":
                    return currentValue < alertValue;
                case "<=":
                    return currentValue <= alertValue;
                case "=":
                    return currentValue == alertValue;
                case ">":
                    return currentValue > alertValue;
                case ">=":
                    return currentValue >= alertValue;
                case "<>":
                    return currentValue != alertValue;
                default:
                    return false;
            }
        }

        private string ConvertParameterToColumn(string propertyName)
        {
            switch (propertyName)
            {
                case "Kurs ostatni":
                    return "Last";
                case "Kurs otwarcia":
                    return "Open";
                case "Wolumen obr.":
                    return "Volume";
                case "Zmiana":
                    return "Change";
                default:
                    return String.Empty;
            }
        }

        private void ShowNotification(string name, double currentValue, PocketGPWDataSet.AlertsRow alert)
        {
            if (Focused)
                return;

            if (_notification.Visible)
                _notification.Visible = false;

            // text
            string text = "<html><body>" + 
                "<b>" + name + "</b>:<br/>" + 
                alert["Parameter"] +
                " " + alert["Comparision"] +
                " " + alert["Value"] +
                " <font color=\"#FF0000\">(" + currentValue + ")</font>" + 
                "</body></html>";

            _notification.Text = text;
            _notification.Visible = true;
        }

        private void StatusBarVisibility(bool visible)
        {
            _sb.Visible = visible;
        }

        private void StatusBarText(string text)
        {
            _sb.Text = text;
        }

        private void _dg_DoubleClick(object sender, EventArgs e)
        {
            StockView();
        }

        private void _miSettings_Click(object sender, EventArgs e)
        {
            using (SettingsEditor se = new SettingsEditor())
            {
                se.ShowDialog(this, " - opcje");
            }
        }

        private void _miRefresh_Click(object sender, EventArgs e)
        {
            if (!_threadInProgress)
                _forceThreadRefresh = true;
        }        
    }
}